/*
 * 定时控制工具函数
 * 包含防抖、节流、延迟执行等与时间相关的控制函数
 */

import { timerManager } from '../common/timer-manager';

/**
 * **防抖函数**
 * @description 用于限制函数在一定时间内只能执行一次，若在短时间内连续调用则只执行最后一次调用
 * @template {(...args: any[]) => any} T - 被防抖函数类型
 * @param {T} func - 需要防抖的函数
 * @param {number} delay - 防抖延迟时间(毫秒)
 * @param {boolean} [immediate=false] - 是否立即执行(leading edge触发)，即第一次调用时立即执行，后续调用防抖处理
 * @returns {(...args: Parameters<T>) => void & { cancel: () => void }} 带取消功能的防抖函数
 * @example
 * // 基础用法
 * const search = debounce((keyword: string) => {
 *   console.log(`Searching for: ${keyword}`);
 * }, 300);
 * // 连续调用只会在最后一次调用后300ms执行
 * search('a');
 * search('ab');
 * search('abc'); // 只有这次会在300ms后执行
 * @example
 * // 立即执行模式
 * const submitForm = debounce((data: FormData) => {
 *   api.submit(data);
 * }, 500, true);
 * // 立即执行第一次调用，后续调用会防抖
 * submitForm(formData); // 立即执行
 * submitForm(formData); // 被防抖
 * @example
 * // 手动取消防抖
 * const resizeHandler = debounce(() => {
 *   console.log('Window resized');
 * }, 200);
 * window.addEventListener('resize', resizeHandler);
 * // 不需要时取消防抖
 * window.removeEventListener('resize', resizeHandler);
 * resizeHandler.cancel();
 */
export function debounce<T extends (...args: any[]) => any>(
  func: T,
  delay: number,
  immediate: boolean = false
): ((...args: Parameters<T>) => void) & { cancel: () => void } {
  let timeoutId: number | undefined;

  const debounced = (...args: Parameters<T>): void => {
    if (timeoutId !== undefined) {
      timerManager.removeTimer(timeoutId);
    }

    // 立即执行模式且无等待中定时器
    if (immediate && timeoutId === undefined) {
      func(...args);
    }

    timeoutId = timerManager.addTimer(delay, () => {
      if (!immediate) {
        func(...args);
      }
      timeoutId = undefined;
    });
  };

  // 取消防抖函数
  debounced.cancel = (): void => {
    if (timeoutId !== undefined) {
      timerManager.removeTimer(timeoutId);
      timeoutId = undefined;
    }
  };

  return debounced;
}

/**
 * 节流函数
 * @description 限制函数在指定时间内最多执行一次
 * @template {(...args: any[]) => any} T - 被节流函数类型
 * @param {T} func - 需要节流的函数
 * @param {number} delay - 节流时间间隔(毫秒)
 * @param {object} [options] - 配置选项
 * @param {boolean} [options.leading=true] - 是否在节流时间段的开始执行一次
 * @param {boolean} [options.trailing=true] - 是否在节流时间段的结束执行一次
 * @returns {(...args: Parameters<T>) => void & { cancel: () => void }} 带取消功能的节流函数
 * @example
 * // 基础用法
 * const resizeHandler = throttle(() => {
 *   console.log('Window resized');
 * }, 300);
 * window.addEventListener('resize', resizeHandler);
 * @example
 * // 只在开始执行
 * const search = throttle((keyword: string) => {
 *   api.search(keyword);
 * }, 500, { leading: true, trailing: false });
 * @example
 * // 只在结束执行
 * const saveInput = throttle((value: string) => {
 *   saveToStorage(value);
 * }, 1000, { leading: false, trailing: true });
 * @example
 * // 手动取消节流
 * const scrollHandler = throttle(() => {
 *   console.log('Scrolled');
 * }, 200);
 * window.addEventListener('scroll', scrollHandler);
 * // 不需要时取消节流
 * window.removeEventListener('scroll', scrollHandler);
 * scrollHandler.cancel();
 */
export function throttle<T extends (...args: any[]) => any>(
  func: T,
  delay: number,
  options?: {
    leading?: boolean;
    trailing?: boolean;
  }
): ((...args: Parameters<T>) => void) & { cancel: () => void } {
  let timeoutId: number | undefined;
  let lastExecTime = 0;
  const { leading = true, trailing = true } = options || {};
  let trailingArgs: Parameters<T> | undefined;

  const executeFunction = (...args: Parameters<T>): void => {
    lastExecTime = Date.now();
    func(...args);
  };

  const throttled = (...args: Parameters<T>): void => {
    const currentTime = Date.now();
    const timeSinceLastExec = currentTime - lastExecTime;

    // 首次调用或者距离上次执行超过延迟时间
    if (!lastExecTime && leading) {
      executeFunction(...args);
    } else if (timeSinceLastExec >= delay) {
      // 如果有等待执行的尾调用，先清除
      if (timeoutId !== undefined) {
        timerManager.removeTimer(timeoutId);
        timeoutId = undefined;
      }
      executeFunction(...args);
    } else if (trailing && timeoutId === undefined) {
      // 记录尾调用参数
      trailingArgs = args;
      timeoutId = timerManager.addTimer(delay - timeSinceLastExec, () => {
        if (trailingArgs) {
          executeFunction(...trailingArgs);
          trailingArgs = undefined;
          timeoutId = undefined;
        }
      });
    }
  };

  // 取消节流函数
  throttled.cancel = (): void => {
    if (timeoutId !== undefined) {
      timerManager.removeTimer(timeoutId);
      timeoutId = undefined;
    }
    trailingArgs = undefined;
    // 不重置lastExecTime，保留当前节流状态
  };

  return throttled;
}

/**
 * 一次性执行函数：确保函数只执行一次
 * @template {(...args: any[]) => any} T - 被包装函数类型
 * @param {T} func - 需要限制只执行一次的函数
 * @returns {(...args: Parameters<T>) => ReturnType<T> | undefined} 包装后的函数，第二次调用返回undefined
 * @example
 * const initialize = once(() => {
 *   console.log('Initialized!');
 *   return true;
 * });
 * initialize(); // 输出 'Initialized!' 并返回 true
 * initialize(); // 返回 undefined，不会执行函数体
 */
export function once<T extends (...args: any[]) => any>(
  func: T
): (...args: Parameters<T>) => ReturnType<T> | undefined {
  let hasBeenCalled = false;
  let result: ReturnType<T>;

  return function (...args: Parameters<T>): ReturnType<T> | undefined {
    if (!hasBeenCalled) {
      hasBeenCalled = true;
      result = func(...args);
      return result;
    }
    return undefined;
  };
}

/**
 * 延迟执行函数：创建一个延迟执行的函数
 * @template {(...args: any[]) => any} T - 被延迟执行的函数类型
 * @param {T} func - 需要延迟执行的函数
 * @param {number} delay - 延迟时间(毫秒)
 * @returns {(...args: Parameters<T>) => number} 包装后的函数，返回定时器ID
 * @example
 * const delayedLog = delay((message) => {
 *   console.log(message);
 * }, 1000);
 * const timerId = delayedLog('Hello after 1 second');
 * // 可以通过 timerManager.removeTimer(timerId) 取消执行
 */
export function delay<T extends (...args: any[]) => any>(
  func: T,
  delay: number
): (...args: Parameters<T>) => number {
  return function (...args: Parameters<T>): number {
    return timerManager.addTimer(delay, () => func(...args));
  };
}

/**
 * 异步延迟函数：返回一个Promise，在指定时间后解析
 * @param {number} ms - 延迟的毫秒数
 * @returns {Promise<void>} 延迟后的Promise
 * @example
 * async function demo() {
 *   console.log('Start');
 *   await sleep(1000);
 *   console.log('After 1 second');
 * }
 * 
 * demo(); // 执行函数
 */
export function sleep(ms: number): Promise<void> {
  return new Promise(resolve => {
    timerManager.addTimer(ms, resolve);
  });
}